﻿using System;
using System.Runtime.Serialization;
using CI.Interfaces;
using CI.Organism;

namespace CI.Organism
{
    /// <summary>
    /// Heart beat offset, this requires that the heart beat counter for a Organism
    /// is at specefied number from organisms start.
    /// Requirement item that has to be met before the Gene action can be activated
    /// </summary>
    [DataContract]
    public class HBOffsetTime : Requirement, IRequirement
    {
        /// <summary>
        /// Heart beat offset
        /// </summary>
        [DataMember]
        public uint HBOffset { get; set; }


        /// <summary>
        /// Heart beat offset, this requires that the heart beat counter for a Organism
        /// is at specefied number from organisms start
        /// </summary>
        /// <param name="organism">Current Organism</param>
        /// <param name="hbOffset">The Heart beat offset value</param>
        /// <param name="comparisonType">Comparison Type</param>
        /// <param name="guid">guid</param>
        public HBOffsetTime(IOrganism organism, UInt32 hbOffset, ComparisonType comparisonType, Guid guid)
            : base(guid)
        {
            #region Argument check
            if (organism == null)
                throw new ArgumentException("Organism");
            #endregion

            // save the Organism;
            Organism = organism;

            // save the offset time
            HBOffset = hbOffset;

            // save the comparison type
            ComparisonType = comparisonType;

        }

        /// <summary>
        /// Generic constructor only to be used by mutation
        /// It set the HNOffset to 0, you should mutate this straight away
        /// </summary>
        /// <param name="organism">Organism</param>
        /// <param name="comparisonType">Comparison Type</param>
        /// <param name="guid">guid</param>
        /// todo : change this to internal and fix the caller
        public HBOffsetTime(IOrganism organism, ComparisonType comparisonType, Guid guid) : this(organism, 0, comparisonType, guid) { }

        /// <summary>
        /// Does the requirement type meet the requirements
        /// </summary>
        /// <returns>True if it meets the requirements else false</returns>
        public override bool State()
        {
            // if comparison type Equals
            if (ComparisonType == ComparisonType.Equal)
                return (Organism.HeartbeatCount == HBOffset);

            // if comparison type greater
            if (ComparisonType == ComparisonType.GreaterThan)
                return (Organism.HeartbeatCount > HBOffset);

            // if comparison type less
            if (ComparisonType == ComparisonType.LessThan)
                return (Organism.HeartbeatCount < HBOffset);

            // else didnt make it
            return false;
        }

        /// <summary>
        /// Take this parent Requirement and create a new requirement to be put in the new child organism
        /// Mutate the current requirement and return a new one, deep class level mutation
        /// </summary>
        /// <param name="mutationRate">mutation rate to be applied</param>
        /// <param name="childOrganism">child Organism it will be going into</param>
        /// <returns>new requirement for child</returns>
        public override IRequirement Mutate(double mutationRate, IOrganism childOrganism)
        {
            #region Argument check
            if (childOrganism == null)
                throw new ArgumentException("Organism");
            #endregion

            // create a new requirement
            var childRequirement = new HBOffsetTime(childOrganism, HBOffset, ComparisonType, Guid);

            // change the HBOffset amount, force if empty
            do
            {
                childRequirement.HBOffset = Mutation.MutateCrawlOrLeap(HBOffset, mutationRate);

                // if this.HBOffset is 0 and mutation rate is 0 this will loop for infinity, so create a random new offset
                if ((childRequirement.HBOffset == 0) && (mutationRate == 0) && (HBOffset == 0))
                {
                    // set it for a large mutation rate
                    childRequirement.HBOffset = Mutation.MutateCrawlOrLeap(HBOffset, 1);
                }
            }
            while (childRequirement.HBOffset == 0);

            // change the comparison type
            if (Mutation.MutateState(mutationRate))
            {
                childRequirement.ComparisonType = ((ComparisonType)(new Random().Next(1, Enum.GetValues(typeof(ComparisonType)).Length)));
            }

            return childRequirement;
        }

        /// <summary>To String, show compairson type</summary>
        public override string ToString()
        {
            return base.ToString() + Environment.NewLine +
                   string.Format("Heart Beat Offset : {0}", HBOffset);
        }

        /// <summary>
        /// do a equals comparison of one requirement type to another only comparing the value types, no refference comparisons
        /// </summary>
        /// <param name="requirement">requirement to compare against</param>
        /// <returns>true if equal value types</returns>
        public override bool EqualsValueTypes(IRequirement requirement)
        {
            // compare value
            if (HBOffset != ((HBOffsetTime)requirement).HBOffset)
                return false;

            return base.EqualsValueTypes(requirement);
        }
    }
}